package server;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    private Mapper mapper = new Mapper();

    List<Mapper.Wrapper> wrappers = new ArrayList<>();

    List<Mapper.Context> contexts = new ArrayList<>();

    List<Mapper.Host> hosts = new ArrayList<>();
    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {


        // 加载解析server.xml封装到mapper组件中
        loadServer();
        // 加载解析相关的配置，web.xml
        // loadServlet();
        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize =50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port：" + port);
        System.out.println("=========>>>>>>使用线程池进行多线程改造");
        while(true) {

            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,mapper);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }

    }


    private Map<String,HttpServlet> servletMap = new HashMap<String,HttpServlet>();




    // 加载解析server.xml返回一个mapper对象
    private void loadServer(){


        /**
         *
         * <Server>
         *     <Service>
         *         <Connector port="8080"/>
         *         <Engine>
         *             <Host name="localhost" appBase="E:/lagou/minicat/webapps"/>
         *         </Engine>
         *     </Service>
         * </Server>
         */
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> services = rootElement.selectNodes("Service");
            String name;
            for (Element service : services
                 ) {
                Element connector = (Element) service.selectSingleNode("Connector");//<Connector port="8080"/>
                Element host = (Element) service.selectSingleNode("Engine/Host");//<Host name="localhost" appBase="E:/lagou/minicat/webapps"/>

                name = host.attributeValue("name");// host的name  localhost
                String appBase = host.attributeValue("appBase");// E:/lagou/minicat/webapps 这是加载webapp的路径，拿这先去找web.xml


                // 用appBase 找到rootfile 一个Stringlist里面是E:/lagou/minicat/webapps下面每个app的路径
                File rootfile = new File(appBase);
                File[] files = rootfile.listFiles();
                // files现在就是2个webapp 拿到他们的路径解析他们的web.xml完成mapper的封装

                for (File file : files
                     ) {

                    String path = file.getPath();
                    loadServlet(path);
                    Mapper.Context context = new Mapper().new Context(path, wrappers); // E:/lagou/minicat/webapps/demo1
                    contexts.add(context);
                }

                Mapper.Host hostm = new Mapper().new Host(name, contexts);
                hosts.add(hostm);


//                InputStream rootfile = Files.newInputStream(Paths.get(appBase));
//                rootfile

            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
        mapper.setHosts(hosts);

    }

    /**
     * 加载解析web.xml，初始化Servlet
     *
     *
     * 4.0版本中这个应该在解析server后
     */
    private void loadServlet(String path) {


        String xmlPath = path.concat("\\web.xml");
        // 这里读出来是null
        //InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(xmlPath);// E:/lagou/minicat/webapps/demo1/web.xml
        try {


        InputStream resourceAsStream = Files.newInputStream(Paths.get(xmlPath));
        SAXReader saxReader = new SAXReader();

            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();


            /**
             *
             * <?xml version="1.0" encoding="UTF-8" ?>
             * <web-app>
             *     <servlet>
             *         <servlet-name>demo1</servlet-name>
             *         <servlet-class>servlet.Demo1Servlet</servlet-class>
             *     </servlet>
             *
             *
             *     <servlet-mapping>
             *         <servlet-name>demo1</servlet-name>
             *         <url-pattern>/servlet1</url-pattern>
             *     </servlet-mapping>
             * </web-app>
             */
            List<Element> selectNodes = rootElement.selectNodes("//servlet");


            String servletName;  //  demo1
            String servletClass; //  servlet.Demo1Servlet
            String urlPattern;  //  /servlet1


            for (int i = 0; i < selectNodes.size(); i++) {
                Element element =  selectNodes.get(i);

                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                servletName = servletnameElement.getStringValue();//<servlet-name>demo1</servlet-name>

                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                servletClass = servletclassElement.getStringValue();//<servlet-class>servlet.Demo1Servlet</servlet-class>


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");

                urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();//<url-pattern>/servlet1</url-pattern>
                //servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

                // 成功获取数据开始加到Mapper里
                String[] split = servletClass.split("\\.");
                String classPath = path + "\\" + split[0] + "\\";
                File file=new File(path);
                URL url=file.toURL();
                URL[] urls=new URL[]{url};
                Class c = new URLClassLoader(urls).loadClass(servletClass);

                Servlet servlet = (Servlet) c.getDeclaredConstructor().newInstance();


                String[] split2 = path.split("\\\\");
                String appName = split2[split2.length-1];
               Mapper.Wrapper wrapper = new Mapper().new Wrapper("/"+ appName + urlPattern, (HttpServlet) servlet);
               wrappers.add(wrapper);
            }



        } catch (DocumentException e) {
            e.printStackTrace();
        }  catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
